/***************************************************************************************
 * Copyright 2020 Infineon Technologies AG ( www.infineon.com ).                       *
 * All rights reserved.                                                                *
 *                                                                                     *
 * Licensed  Material-Property of Infineon Technologies AG.                            *
 * This software is made available solely pursuant to the terms of Infineon            *
 * Technologies AG agreement which governs its use. This code and the information      *
 * contained in it are proprietary and confidential to Infineon Technologies AG.       *
 * No person is allowed to copy, reprint, reproduce or publish any part of this code,  *
 * nor disclose its contents to others, nor make any use of it, nor allow or assist    *
 * others to make any use of it - unless by prior Written express authorization of     *
 * Infineon Technologies AG and then only to the extent authorized.                    *
 *                                                                                     *
 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,            *
 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY,           *
 * FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT, ARE DISCLAIMED.  IN NO       *
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,     *
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,                 *
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;         *
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY             *
 * WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR            *
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF              *
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.                                          *
 *                                                                                     *
 ***************************************************************************************/

/**
 * @file   usecase_nvm.c
 * @date   June, 2020
 * @brief  Implementation of NVM use case
 */

#include <stdio.h>

#include "auths_api.h"
#include "auths_config.h"
#include "auths_status.h"
#include "crypto_lib.h"

#include "i2c_bus_command.h"
#include "nvm.h"
#include "helper.h"

extern AuthS_Capability auths_capability;
extern uint8_t g_DevAddr;
extern dwordvec_t gf2nReturnZ;
extern dwordvec_t gf2nCheck;
extern curve_parameter_t ECC_CURVE_163[1];

void nvmtest_write_page(){
	uint8_t nvm_page =0;
	uint8_t bytetowrite=16;
	uint16_t ret;
	uint8_t rd_nvmdata[16]={0};
	uint8_t data[16]={0x1, 0x2, 0x3, 0x4,
					0x5, 0x6, 0x7, 0x8,
					0x9, 0xA, 0xB, 0xC,
					0xD, 0xE, 0xF, 0x10};

	ret = Intf_ReadRegister(nvm_page, rd_nvmdata, bytetowrite);
	if(SDK_INTERFACE_SUCCESS!=ret){
		PRINT("Error: Read NVM failed, ret=0x%x\r\n", ret);
		return;
	}
	PRINT("NVM Page %d (Before):\r\n",nvm_page);
	PrintByteArrayHex(rd_nvmdata, bytetowrite);

	ret = auths_write_nvm(nvm_page, data, bytetowrite/4 );
	if(APP_NVM_SUCCESS != ret){
		PRINT("Error: Write NVM failed, ret=0x%x\r\n", ret);
		return;
	}
	ret = Intf_ReadRegister(nvm_page, rd_nvmdata, bytetowrite);
	if(SDK_INTERFACE_SUCCESS != ret){
		PRINT("Error: Read NVM failed, ret=0x%x\r\n", ret);
		return;
	} else{
		PRINT("NVM Page (after):\r\n", nvm_page);
		PrintByteArrayHex(rd_nvmdata, bytetowrite);
	}
}

void nvmtest_write_1page(){

	uint8_t nvm_page =0;
	uint8_t bytetowrite=4;	
	uint16_t ret;
	uint8_t rd_nvmdata[4]={0};
	uint8_t data[4]={0x1, 0x2, 0x3, 0x4};				

	//Read the data from existing NVM location
	ret = Intf_ReadRegister(nvm_page, rd_nvmdata, bytetowrite);
	if(SDK_INTERFACE_SUCCESS!=ret){
		PRINT("Error: Read NVM failed, ret=0x%x\r\n", ret);
		return;
	}else{
		PRINT("NVM Page %d (Before):\r\n", nvm_page);
		PrintByteArrayHex(rd_nvmdata, bytetowrite);
	}

	//Write to NVM from page 0 with length of 4 bytes (1 page)
	ret = auths_write_nvm(nvm_page, data, 1);
	if(APP_NVM_SUCCESS != ret){
		PRINT("Error: Write NVM failed, ret=0x%x\r\n", ret);
		return;
	}
	//Read the data after NVM writing
	ret = Intf_ReadRegister(nvm_page, rd_nvmdata, bytetowrite);
	if(SDK_INTERFACE_SUCCESS != ret){
		PRINT("Error: Read NVM failed, ret=0x%x\r\n", ret);
		return;
	} else{
		PRINT("NVM Page (after):\r\n", nvm_page);
		PrintByteArrayHex(rd_nvmdata, bytetowrite);
	}

}

void nvmtest_write_clr_userpage(uint16_t start_nvm_page, uint8_t page_count){

	uint16_t ret;
	uint8_t data[656]={0};
	uint8_t rd_nvmdata[656]={0};

	//Create the use case data
	memset(data, 0, sizeof(data));
	
	PRINT("Write all Zero to NVM:\r\n");
	ret = auths_write_nvm(start_nvm_page, data, page_count);
	if(APP_NVM_SUCCESS != ret){
		PRINT("Error: Write NVM failed, ret=0x%x\r\n", ret);
		return;
	}

	ret = auths_read_nvm(start_nvm_page, rd_nvmdata, page_count);
	if(ret != APP_NVM_SUCCESS ){
		PRINT("Error: Read NVM failed, ret=0x%x\r\n", ret);
		return;
	}else{
		//PRINT("NVM Page %d (After):\r\n", start_nvm_page);
		//PrintByteArrayHex(rd_nvmdata, page_count*4);
	}

}

void nvmtest_write_inc_userpage(uint16_t start_nvm_page, uint8_t page_count){

	uint16_t ret;
	uint8_t rd_nvmdata[656]={0};
	uint8_t data[656]={0};

	//Use auths_read_nvm() here instead of using the register to read so that longer length can be read.
	ret = auths_read_nvm(start_nvm_page, rd_nvmdata, page_count);
	if(ret != APP_NVM_SUCCESS ){
		PRINT("Error: Read NVM failed, ret=0x%x\r\n", ret);	
		return;
	}else{
		//PRINT("NVM Page %d (Before):\r\n", start_nvm_page);
		//PrintByteArrayHex(rd_nvmdata, page_count*4);
	}

	//Create the data
	for(uint16_t i=0; i<(page_count*4); i++){
		data[i] = i;
	}

	//Write to NVM from page 0 with length of 8 bytes (2 page)
	ret = auths_write_nvm(start_nvm_page, data, page_count);
	if(APP_NVM_SUCCESS != ret){
		PRINT("Error: Write NVM failed, ret=0x%x %d %d\r\n", ret, start_nvm_page, page_count);
		return;
	}

	//Use auths_read_nvm() here instead of using the register to read so that longer length can be read.
	ret = auths_read_nvm(start_nvm_page, rd_nvmdata, page_count);
	if(ret != APP_NVM_SUCCESS ){
		PRINT("Error: Read NVM failed, ret=0x%x\r\n", ret);	
		return;
	}else{
		//PRINT("NVM Page %d (After):\r\n", start_nvm_page);
		//PrintByteArrayHex(rd_nvmdata, page_count*4);
		uint16_t x=656;
		while(x){
			if(rd_nvmdata[x-1]!= data[x-1]){
				PRINT("Error: Readback data is different at position %d\r\n", x);
			}
			x--;
		}
		if(x==0){
			PRINT("Readback data ok\r\n");
		}
	}

}

void nvmtest_read_page(uint16_t start_page, uint8_t page_count)
{
	uint16_t ret;
	uint8_t nvm_data[656]; //For 5.125Kbits Authenticate S

	memset(nvm_data, 0, sizeof(nvm_data));

	ret = auths_read_nvm(start_page, nvm_data, page_count);
	if (APP_NVM_SUCCESS != ret) {
		PRINT("Error: Read NVM, ret=0x%x", ret);
	} else {
		//PrintByteArrayHex(nvm_data, page_count*4);
	}

}

void nvmtest_lock()
{
	uint8_t page = 0;
	uint16_t ret;

	PRINT("Lock NVM page: %d\r\n", page);
	ret = auths_set_nvm_page_lock(page);
	if (ret != APP_NVM_SUCCESS) {
		PRINT("Error: Set NVM User Page locked failed, ret=0x%x\r\n", ret);
		}
	delay_ms(7);
	PRINT("Read NVM User Page Lock status: \r\n");
	ret = auths_get_nvm_lock_status(page);
	if (ret == APP_NVM_PAGE_LOCKED) {
		PRINT("Error: NVM User Page is locked, ret=0x%x\r\n", ret);
		}

}
void nvmtest_mac()
{
	uint16_t page = 0;
	uint16_t ret;
	uint8_t errcnt=0;

	for(page=0; page<CONFIG_NVM_PAGE_COUNT; page++){
		//PRINT("Check MAC for NVM page: %d\r\n", page);
		ret = auths_exe_mac_nvm(page, DEFAULT_MAC_GROUP);
		if (ret != APP_ECC_SUCCESS) {
			PRINT("Error: MAC verify failed, ret=0x%x\r\n", ret);
			errcnt++;
		} else {
			//PRINT("Info: MAC page %d passed.\r\n",page);
			//PrintByteArrayHex(mac_value, MAC_BYTE_LEN);
		}
	}

	if(errcnt==0){
		PRINT("MAC verify passed\r\n");
	}
}

void nvmtest_resetlock()
{
	uint16_t ret;
	uint8_t data[UID_BYTE_LEN];
	mac_t mac_result;
	uint8_t mac_byte[MAC_BYTE_LEN];
	//uint8_t j;
	uint16_t page=0;

	ret = Intf_ReadRegister( AUTHS_SFR_LOCK_STS, data, 1);
	if (SDK_INTERFACE_SUCCESS != ret) {
		PRINT("Read lock status failed, ret=0x%x\r\n", ret);
		return;
	}
	if (((data[0] >> BIT_IFX_USERNVM_RESET_CONFIG) & 0x01) == 0) {
		PRINT("IFX user NVM reset feature is not enabled!!\r\n");
		return;
	}

	do{
		PRINT("Do Authentication.\r\n");

		// read uid

#if (ENABLE_I2C_TEST==1)
	ret = Intf_ReadRegister(AUTHS_UID_ADDR, data, 12);
	if (SDK_INTERFACE_SUCCESS != ret) {
		PRINT("Error: Read UID failed, ret=0x%x\r\n", ret);
		return;
	}
#endif		

		ret = auths_exe_ecc(0, 0);
		if (EXE_SUCCESS  != ret) {
			PRINT("Error: Authentication failed, ret=0x%x\r\n", ret);
			return;
		} else {
			PRINT("Authentication PASS.\r\n");
		}

		// MAC password
		uint32_t Reset_Password[3];
		Get_Reset_Password(Reset_Password);

		PRINT("MAC reset password.\r\n");
		do_mac( mac_result, Reset_Password, gf2nReturnZ, gf2nCheck, 0, ECC_CURVE_163, MAC_GROUP_0);

		// Send MACCR5
		PRINT("Send MACCR5 with Mac Result.\r\n");
		MacData2Byte(mac_result, mac_byte);

		delay_ms(15);

		PRINT("Activate device and do Host Authentication.\r\n");


#if (HOST_AUTHENTICATION_FEATURE_SUPPORTED == 1)
		ret = auths_exe_host_authentication();
		if(APP_HA_SUCCESS != ret){
			PRINT("Error: Host Auth failed, ret=0x%x\r\n", ret);
			return;
		}
#endif
		PRINT("Read NVM User Page Lock status: \r\n");
		ret = auths_get_nvm_lock_status(page);
		if(ret != APP_NVM_PAGE_NOT_LOCKED){
			PRINT("Error: Unlock NVM failed, ret=0x%x\r\n", ret);
		}
		PRINT("NVM User Page unlocked successfully: \r\n");	
	}while(0);
}
//更改iic地址
void nvmtest_i2c_changeDevAddr(uint8_t current_address, uint16_t nw_newDevAddr)
{
	uint16_t ret;
	uint8_t nw_rdDevAddr;

    ret = auths_get_i2c_address(current_address, &nw_rdDevAddr);
	if(SDK_SUCCESS!= ret){
		PRINT("Error: Read address, ret=0x%x\r\n", ret);
	}
    PRINT("Device address: 0x%.4X\r\n",nw_rdDevAddr);

    ret = auths_set_i2c_address(current_address, nw_newDevAddr);
	if(SDK_SUCCESS!= ret){
		PRINT("Error: Set address, ret=0x%x\r\n", ret);
	}

    delay_ms(7);

    ret = auths_get_i2c_address(nw_newDevAddr, &nw_rdDevAddr);
	if(SDK_SUCCESS!= ret){
		PRINT("Error: Get address, ret=0x%x\r\n", ret);
	}

	PRINT("After changing, new address: 0x%.4X\r\n",nw_rdDevAddr);
	if(nw_rdDevAddr != nw_newDevAddr){
		PRINT("Mismatch: device address.\r\n");
	}
}
